#include <d3d9.h>
#include <d3dx9.h>
#include <windows.h>
#include "Game.h"
#include "Trace.h"
#include <time.h>

#include "Utility.h"

/**--------------------------------------------------------
*Description : Constructor
*Method name : Game
*Parameters :  HINSTANCE hInstance, LPWSTR Name, int Mode, int IsFullScreen, int FrameRate
*Return value :
--------------------------------------------------------**/
Game::Game(HINSTANCE hInstance, LPWSTR Name, int Mode, int IsFullScreen, int FrameRate)
{
	d3d = new DirectX();
	/*_d3d = NULL;
	_d3ddv = NULL;
	_BackBuffer = NULL;*/

	/*_di = NULL;
	_Keyboard = NULL;*/

	_Mode = Mode;
	_SetScreenDimension(Mode);
	_Name = Name;
	_IsFullScreen = IsFullScreen;
	_FrameRate = FrameRate;

	_hInstance = hInstance;


	p_input = new DxInput();
	p_time = new Timer();

	srand((unsigned)(time(NULL)));
}


/**--------------------------------------------------------
*Description : Thiet dat che do man hinh
*Method name : _SetScreenDimension
*Parameters :  int Mode
*Return value :void
--------------------------------------------------------**/
void Game::_SetScreenDimension(int Mode)
{
	switch (Mode)
	{
	case GL_GAME_SCREEN_RESOLUTION_640_480_24:
		_ScreenWidth = 640;
		_ScreenHeight = 480;
		_Depth = 24;
		_BackBufferFormat = D3DFMT_X8R8G8B8;

		break;

	case GL_GAME_SCREEN_RESOLUTION_512_512_24:
		_ScreenWidth = 512;
		_ScreenHeight = 512;
		_Depth = 24;

		_BackBufferFormat = D3DFMT_X8R8G8B8;
		break;

	case GL_GAME_SCREEN_RESOLUTION_480_512_24:
		_ScreenWidth = 480;
		_ScreenHeight = 512;
		_Depth = 24;

		_BackBufferFormat = D3DFMT_X8R8G8B8;
		break;

	default:
		break;
	}
}


/**--------------------------------------------------------
*Description : Khoi tao cua so man hinh
*Method name : _InitWindow
*Parameters :
*Return value :void
--------------------------------------------------------**/
void Game::_InitWindow()
{
	MSG msg;
	WNDCLASSEX wc;
	wc.cbSize = sizeof(WNDCLASSEX);
	ZeroMemory(&msg, sizeof(msg));

	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.hInstance = _hInstance;

	wc.lpfnWndProc = (WNDPROC)Game::_WinProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hIcon = NULL;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = _Name;
	wc.hIconSm = NULL;

	RegisterClassEx(&wc);

	DWORD style;
	if (_IsFullScreen)
		style = WS_EX_TOPMOST | WS_VISIBLE | WS_POPUP;
	else
		style = WS_OVERLAPPEDWINDOW;

	_hWnd =
		CreateWindow(
		_Name,
		_Name,
		style,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		_ScreenWidth,
		_ScreenHeight,
		NULL,
		NULL,
		_hInstance,
		this//NULL
		);

	if (!_hWnd)
	{
		trace(L"[ERROR] Failed to created window!");
		DWORD ErrCode = GetLastError();
	}

	ShowWindow(_hWnd, SW_SHOWNORMAL);
	UpdateWindow(_hWnd);
}


/**--------------------------------------------------------
*Description : Khoi tao Direct3D
*Method name : _InitDirectX
*Parameters :
*Return value :void
--------------------------------------------------------**/
void Game::_InitDirectX()
{
	/*_d3d = Direct3DCreate9(D3D_SDK_VERSION);
	D3DPRESENT_PARAMETERS d3dpp;

	ZeroMemory( &d3dpp, sizeof(d3dpp) );

	d3dpp.Windowed = _IsFullScreen?FALSE:TRUE;

	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

	d3dpp.BackBufferFormat = _BackBufferFormat;
	d3dpp.BackBufferCount = 1;
	d3dpp.BackBufferHeight = _ScreenHeight;
	d3dpp.BackBufferWidth = _ScreenWidth;

	_d3d->CreateDevice(
	D3DADAPTER_DEFAULT,
	D3DDEVTYPE_HAL,
	_hWnd,
	D3DCREATE_SOFTWARE_VERTEXPROCESSING,
	&d3dpp,
	&_d3ddv);

	if (_d3ddv==NULL)
	{
	trace(L"[ERROR] Failed to created device!");
	}

	_d3ddv->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&_BackBuffer);*/
	d3d->Init(_hWnd, _ScreenWidth, _ScreenHeight, _IsFullScreen);
}


/**--------------------------------------------------------
*Description : Khoi tao doi tuong ban phim
*Method name : _InitKeyboard
*Parameters :
*Return value :void
--------------------------------------------------------**/
void Game::_InitKeyboard()
{
	//   HRESULT 
	//	hr = DirectInput8Create
	//		( 
	//			_hInstance, 
	//			DIRECTINPUT_VERSION, 
	//			IID_IDirectInput8, (VOID**)&_di, NULL 
	//		);

	//// TO-DO: put in exception handling
	//if (hr!=DI_OK) return;

	//trace(L"DirectInput has been created");

	//hr = _di->CreateDevice(GUID_SysKeyboard, &_Keyboard, NULL); 
	//
	//// TO-DO: put in exception handling
	//if (hr!=DI_OK) return;

	//trace(L"DirectInput keyboard has been created");

	//   // Set the data format to "keyboard format" - a predefined data format 
	//   //
	//   // A data format specifies which controls on a device we
	//   // are interested in, and how they should be reported.
	//   //
	//   // This tells DirectInput that we will be passing an array
	//   // of 256 bytes to IDirectInputDevice::GetDeviceState.

	//hr = _Keyboard->SetDataFormat(&c_dfDIKeyboard);

	//trace(L"SetDataFormat for keyboard successfully");

	//hr = _Keyboard->SetCooperativeLevel(_hWnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); 

	//trace(L"SetCooperativeLevel for keyboard successfully");

	//   // IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
	//   //
	//   // DirectInput uses unbuffered I/O (buffer size = 0) by default.
	//   // If you want to read buffered data, you need to set a nonzero
	//   // buffer size.
	//   //
	//   // Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
	//   //
	//   // The buffer size is a DWORD property associated with the device.
	//   DIPROPDWORD dipdw;

	//   dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
	//   dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
	//   dipdw.diph.dwObj        = 0;
	//   dipdw.diph.dwHow        = DIPH_DEVICE;
	//   dipdw.dwData            = GL_KEYBOARD_BUFFER_SIZE; // Arbitary buffer size

	//trace(L"SetProperty for keyboard successfully");

	//   hr = _Keyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph );
	//if (hr!=DI_OK) return;

	//hr = _Keyboard->Acquire(); 
	//if (hr!=DI_OK) return;

	//trace(L"Keyboard has been acquired successfully");

	p_input->_InitDirectInput(_hInstance, _hWnd);
}


/**--------------------------------------------------------
*Description : Khoi tao Game
*Method name : Init
*Parameters :
*Return value :void
--------------------------------------------------------**/
void Game::Init()
{
	_InitWindow();
	_InitDirectX();
	_InitKeyboard();
	//LoadResources(_d3ddv);
	LoadResources(d3d->GetDevice(), d3d->GetBackBuffer());
}


/**--------------------------------------------------------
*Description : Lay trang thai cua ban phim
*Method name : _ProcessKeyBoard
*Parameters :
*Return value :void
--------------------------------------------------------**/
void Game::_ProcessKeyBoard()
{
	//// Collect all key states first
	//_Keyboard->GetDeviceState( sizeof(_KeyStates), _KeyStates);

	//if(!SUCCEEDED(_Keyboard->GetDeviceState( sizeof(_KeyStates), _KeyStates)))
	//_Keyboard->Acquire();


	//if (IsKeyDown(DIK_ESCAPE)) 
	//{
	//	trace(L"Escape key pressed!");
	//	PostMessage(_hWnd,WM_QUIT,0,0);
	//}

	//// Collect all buffered events
	//DWORD dwElements = GL_KEYBOARD_BUFFER_SIZE;
	//HRESULT hr = _Keyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), _KeyEvents, &dwElements, 0 );

	//// Scan through all data, check if the key is pressed or released
	//for( DWORD i = 0; i < dwElements; i++ ) 
	//{
	//	int KeyCode = _KeyEvents[i].dwOfs;
	//	int KeyState = _KeyEvents[i].dwData;
	//	if ( (KeyState & 0x80) > 0)
	//		OnKeyDown(KeyCode);
	//	else 
	//		OnKeyUp(KeyCode);
	//}

	p_input->_Process_KeyBoard();

}


/**--------------------------------------------------------
*Description : Run Game
*Method name : Run
*Parameters :
*Return value :void
--------------------------------------------------------**/
void Game::Run()
{
	MSG msg;
	int done = 0;
	DWORD frame_start = GetTickCount64();

	//DWORD tick_per_frame = 500 / _FrameRate;	//CPU chiem 25%
	DWORD tick_per_frame = 100 / _FrameRate;	//CPU chiem <15%

	trace(L">>> Main game loop has been started");

	while (!done)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT) done = 1;

			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}



		DWORD now = GetTickCount64();
		_DeltaTime = now - frame_start;
		if (_DeltaTime > tick_per_frame)
		{

			UpdateGame();
			frame_start = now;

		}
		_RenderFrame();

		//p_time->LimitFPS(80);
		//TPF = p_time->TPF;
		//_DeltaTime = TPF * 1000;
		//_RenderFrame();

		_ProcessKeyBoard();

		//ProcessInput(_d3ddv, _DeltaTime);
		ProcessInput(d3d->GetDevice(), _DeltaTime);
	}

	trace(L"Main game loop has ended");
}


/**--------------------------------------------------------
*Description : Render Frame
*Method name : _RenderFrame
*Parameters :
*Return value :void
--------------------------------------------------------**/
void Game::_RenderFrame()
{
	/*if (_d3ddv->BeginScene())
	{
	update(_d3ddv, _DeltaTime);
	RenderFrame(_d3ddv, _DeltaTime);
	_d3ddv->EndScene();
	}
	_d3ddv->Present(NULL,NULL,NULL,NULL);*/
	if (d3d->GetDevice()->BeginScene())
	{

		RenderFrame(d3d->GetDevice(), d3d->GetBackBuffer(), _DeltaTime);
		d3d->GetDevice()->EndScene();
	}
	d3d->GetDevice()->Present(NULL, NULL, NULL, NULL);

}


/**--------------------------------------------------------
*Description : Render Frame
*Method name : RenderFrame
*Parameters :
*Return value :void
--------------------------------------------------------**/
void Game::RenderFrame(LPDIRECT3DDEVICE9 d3ddv, LPDIRECT3DSURFACE9 backBuffer, int Delta)
{
	d3ddv->ColorFill(backBuffer, NULL, D3DCOLOR_XRGB(0, 0, 0));
}


/**--------------------------------------------------------
*Description : Load Resources
*Method name : LoadResources
*Parameters :  LPDIRECT3DDEVICE9 d3ddv
*Return value :void
--------------------------------------------------------**/
void Game::LoadResources(LPDIRECT3DDEVICE9 d3ddv, LPDIRECT3DSURFACE9 BackBuffer) { }


/**--------------------------------------------------------
*Description : Process Input
*Method name : ProcessInput
*Parameters :  LPDIRECT3DDEVICE9 d3ddv, int Delta
*Return value :void
--------------------------------------------------------**/
void Game::ProcessInput(LPDIRECT3DDEVICE9 d3ddv, int Delta) { }


/**--------------------------------------------------------
*Description : Huy game
*Method name : ~Game
*Parameters :
*Return value :
--------------------------------------------------------**/
Game::~Game()
{
	/*if (_d3ddv!=NULL) _d3ddv->Release();
	if (_d3d!=NULL) _d3d->Release();*/
	delete d3d;
	//d3d->Shutdown();

	/*if( _Keyboard )
	{
	_Keyboard->Unacquire();
	_Keyboard->Release();
	}

	if (_di) _di->Release();*/

	p_input->_KillDirectInput();
}






/**--------------------------------------------------------
*Description : Nhan tin nhan tu he thong
*Method name : _WinProc
*Parameters :  HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam
*Return value :void
--------------------------------------------------------**/
LRESULT CALLBACK Game::_WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}

	return 0;
}


void Game::update(LPDIRECT3DDEVICE9 d3ddv, LPDIRECT3DSURFACE9 BackBuffer, int Delta)
{

}

void Game::End()
{
	d3d->Shutdown();
	delete d3d;
	p_input->_KillDirectInput();
	delete p_input;

}

void Game::UpdateGame()
{
	update(d3d->GetDevice(), d3d->GetBackBuffer(), _DeltaTime);
}